package com.maimao.kill.service;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baidu.fsg.uid.impl.CachedUidGenerator;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.maimao.core.enums.ExceptionEnum;
import com.maimao.core.exception.MaiMaoDaoException;
import com.maimao.core.model.resp.PageResult;
import com.maimao.core.model.resp.Result;
import com.maimao.kill.client.GoodsClient;
import com.maimao.kill.mapper.KillApplyMapper;
import com.maimao.kill.mapper.KillGoodsMapper;
import com.maimao.kill.mapper.ScheduleMapper;
import com.maimao.model.goods.GoodsSku;
import com.maimao.model.kill.ApplySku;
import com.maimao.model.kill.KillApply;
import com.maimao.model.kill.KillGoods;
import com.maimao.model.kill.Schedule;
import com.maimao.model.kill.enums.KillApplyStatusEnum;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.maimao.core.constant.Constants.ONE;

/**
 * 1、定时任务每15分钟审核一次，一次审核100(可指定)个未审核记录，审核失败或其他无法预期的情况修改状态转人工审核 2、每天23点30分，清理次日排期未审核的记录，修改状态为未通过
 * 3、每天22点00分，如果还有系统在1个半小时内无法审核的记录，则邮件通知负责人进行人工审核
 *
 * @author MaoLin Wang
 * @date 2021/4/8 4:39 下午
 */
@Service
public class KillApplyService {

    @Autowired
    private KillApplyMapper killApplyMapper;

    @Autowired
    private CachedUidGenerator cachedUidGenerator;

    @Autowired
    private ScheduleMapper scheduleMapper;

    @Autowired
    private KillGoodsMapper killGoodsMapper;

    @Autowired
    private KillApplyService killApplyService;

    @Autowired
    private GoodsClient goodsClient;


    /**
     * 新增参与秒杀申请记录
     */
    @Transactional(rollbackFor = MaiMaoDaoException.class)
    public List<ApplySku> apply(List<KillApply> killApplyList) {
        KillApply temp = killApplyList.get(0);
        List<Long> skuIds = querySkuIdsByAcSch(temp.getActivityId(), temp.getScheduleId());
        if (ObjectUtil.isNotEmpty(skuIds)) {
            //查询是否存在已经申请过的sku，返回前段提示重新选择
            List<ApplySku> appliedSku = killApplyList.stream()
                    .flatMap(killApply -> JSONUtil.toList(killApply.getParams(), ApplySku.class).stream())
                    .filter(applySku -> skuIds.contains(applySku.getId())).collect(Collectors.toList());
            if (ObjectUtil.isNotEmpty(appliedSku)) {
                return appliedSku;
            }
        }

        //这里都是用户手动批量申请，因此每个KillApply的活动id和排期id都相同
        Schedule schedule = scheduleMapper.selectById(temp.getScheduleId());
        killApplyList.forEach(item -> {
            item.setId(cachedUidGenerator.getUID());
            item.setScStartTime(schedule.getStartTime());
            item.setScEndTime(schedule.getEndTime());
        });
        killApplyMapper.insertBatch(killApplyList);
        return null;
    }


    /**
     * 批量更新
     *
     * @param killApplyList
     */
    @Transactional(rollbackFor = MaiMaoDaoException.class)
    public void updateBatch(List<KillApply> killApplyList) {
        if (ObjectUtil.isNotEmpty(killApplyList)) {
            killApplyList = killApplyList.stream().filter(Objects::nonNull).collect(Collectors.toList());
            if (ObjectUtil.isNotEmpty(killApplyList)) {
                killApplyMapper.updateBatch(killApplyList);
            }
        }
    }

    /**
     * 商家撤回申请
     *
     * @param id
     */
    @Transactional(rollbackFor = MaiMaoDaoException.class)
    public Result back(Long id) {
        KillApply killApply = killApplyMapper.selectById(id);
        if (killApply.getStatus().equals(KillApplyStatusEnum.UN_CHECK.getCode())) {
            killApplyMapper.updateCancel(id, 1);
            return Result.ok("撤回成功");
        }
        return Result.notValid("商品已审核，不可撤回！");
    }

    /**
     * 根据活动id和排期id查询审核通过或未审核的申请记录的skuId集合
     *
     * @return 找不到则返回null
     */
    public List<Long> querySkuIdsByAcSch(Integer activityId, Long scheduleId) {
        List<String> paramsList = killApplyMapper.selectNotFailParamsByAcIdAndSId(activityId, scheduleId);
        if (ObjectUtil.isNotEmpty(paramsList)) {
            List<Long> skuIds = paramsList.stream()
                    .flatMap(
                            param -> JSONUtil.toList(param, ApplySku.class).stream()
                    )
                    .map(ApplySku::getId)
                    .collect(Collectors.toList());
            return skuIds;
        }
        return null;
    }

    public PageResult<KillApply> query(KillApply killApply, Integer page, Integer size) {
        PageHelper.startPage(page, size);
        List<KillApply> killApplies;
        if (killApply.getId() != null) {
            killApplies = killApplyMapper.selectByIdWithAcName(killApply.getId());
        } else {
            killApplies = killApplyMapper.query(killApply);
        }
        return new PageResult<>(killApplies);
    }

    /**
     * 审核申请
     *
     * @param killApplyList
     */
    @GlobalTransactional(rollbackFor = MaiMaoDaoException.class)
    @Transactional(rollbackFor = MaiMaoDaoException.class)
    public void checkApply(List<KillApply> killApplyList) {
        killApplyService.updateBatch(killApplyList);
        List<Long> okIdList = killApplyList.stream().filter(killApply -> killApply.getStatus().equals(KillApplyStatusEnum.OK.getCode())).map(KillApply::getId).collect(Collectors.toList());
        if (ObjectUtil.isNotEmpty(okIdList)) {
            //添加到秒杀商品中
            List<KillApply> killApplies = killApplyMapper.selectByIdBatch(okIdList);

            List<KillGoods> killGoodsList = Lists.newArrayListWithCapacity(killApplies.size() * 5);
            List<GoodsSku> skusList = Lists.newArrayListWithCapacity(killApplies.size() * 5);
            for (KillApply killApply : killApplies) {
                List<ApplySku> applySkus = JSONUtil.toList(killApply.getParams(), ApplySku.class);
                if (ObjectUtil.isNotEmpty(applySkus)) {

                    List<Long> skuIds = applySkus.stream().map(ApplySku::getId).collect(Collectors.toList());
                    Result<List<GoodsSku>> skuKillInfoBatch = goodsClient.getSkuKillInfoBatch(skuIds);
                    Map<Long, GoodsSku> skuMap = Maps.newHashMapWithExpectedSize(skuIds.size());
                    if (skuKillInfoBatch != null && ObjectUtil.isNotEmpty(skuKillInfoBatch.getData())) {
                        List<GoodsSku> goodsSkus = skuKillInfoBatch.getData();
                        skuMap = goodsSkus.stream().collect(Collectors.toMap(GoodsSku::getId, sku -> sku));
                    }
                    Map<Long, GoodsSku> finalSkuMap = skuMap;
                    applySkus.stream().forEach(
                            sku -> {
                                GoodsSku goodsSku = finalSkuMap.get(sku.getId());
                                if (goodsSku == null) {
                                    throw new MaiMaoDaoException(ExceptionEnum.SKU_NOT_EXIST);
                                }
                                KillGoods killGoods = KillGoods.builder()
                                        .activityId(killApply.getActivityId())
                                        .discount(killApply.getDiscount())
                                        .startTime(killApply.getScStartTime())
                                        .endTime(killApply.getScEndTime())
                                        .goodsId(killApply.getGoodsId())
                                        .killPrice(getKillPrice(killApply, sku))
                                        .shopId(killApply.getShopId())
                                        .rawPrice(sku.getPrice())
                                        .scheduleId(killApply.getScheduleId())
                                        .status(ONE)
                                        .skuId(sku.getId())
                                        .specIndexes(goodsSku.getSpecIndexes())
                                        .skuName(sku.getName())
                                        .stock(sku.getStock())
                                        .pic(sku.getPic())
                                        .giveIntegral(goodsSku.getGiveIntegral())
                                        .skuParam(goodsSku.getSpec())
                                        .specStr(goodsSku.getSpecStr())
                                        .build();
                                killGoodsList.add(killGoods);
                                skusList.add(GoodsSku.builder().id(sku.getId())
                                        .killPrice(killGoods.getKillPrice())
                                        .killStartTime(killGoods.getStartTime())
                                        .killEndTime(killGoods.getEndTime())
                                        .secKill(ONE).build());
                            }
                    );
                }

            }
            if (ObjectUtil.isNotEmpty(killGoodsList)) {
                killGoodsMapper.saveBatch(killGoodsList);
                goodsClient.updateSkuBatch(skusList);
            }
        }
    }

    private BigDecimal getKillPrice(KillApply killApply, ApplySku sku) {
        return sku.getPrice().multiply(BigDecimal.valueOf(killApply.getDiscount() * 0.1)).setScale(0, RoundingMode.HALF_UP);
    }

    /**
     * 查询dayLimit日(按照排期开始时间)内该商品是否报名过（未审核失败）
     *
     * @param activityId  活动id
     * @param goodsId     商品id
     * @param scStartTime 当前申请排期开始时间
     * @param dayLimit    期限
     */
    public List<KillApply> selectApplied(Integer activityId, Long goodsId, LocalDateTime scStartTime, Integer dayLimit) {
        LocalDateTime fromTime = scStartTime.plusDays(-dayLimit);
        LocalDateTime toTime = scStartTime;

        return killApplyMapper.selectDayLimitApplied(activityId, goodsId, fromTime, toTime, KillApplyStatusEnum.FAIL.getCode());
    }
}
